package com.atguigu.tingshu.account.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.MqOpsService;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;

/**
 * Description:
 *
 * @author Yanxinhao on 2025/1/13
 */
@Slf4j
@Service
public class MqOpsServiceImpl implements MqOpsService {
    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private UserAccountService userAccountService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 注册用户的时候用户account初始化
     * @param context
     */
    @Override
    public void userAccountInit(String context) {
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(Long.parseLong(context));
        userAccountMapper.insert(userAccount);
    }


    /**
     * 用户进行消费活动的时候，余额的扣减
     * @param orderNo 订单号
     */
    @Override
    public void minusAccountMoney(String orderNo) {

        //0.我们在这里需要考虑到重复消费问题
        //如果这里多条线程进来并发，那么就有可能会出现订单的重复消费问题，导致用户的余额重复扣款
        //因此我们需要在进行真正金额扣减的时候，加一把分布式锁

        //1.我们在进行判断余额是否足够以及账户金额锁定的时候，就将我们的锁定金额数据对象存入了redis当中，现在我们从redis中取出
        String accountLockResultKey = "user:account:lockmoney:data:" + orderNo;
        String accountLockVoStr = redisTemplate.opsForValue().get(accountLockResultKey);
        AccountLockResultVo accountLockResultVo = JSONObject.parseObject(accountLockVoStr, AccountLockResultVo.class);
        //获取用户id
        Long userId = accountLockResultVo.getUserId();
        //获取锁定金额
        BigDecimal amount = accountLockResultVo.getAmount();
        //获取锁定信息
        String content = accountLockResultVo.getContent();

        //防止重复消费，加入分布式锁
        String accountMoneyMinusRepeatLockKey = "account:money:minus:repeat:" + orderNo;
        try {
            Boolean isFirstTime = redisTemplate.opsForValue().setIfAbsent(accountMoneyMinusRepeatLockKey, "1");
            if (isFirstTime){
                //2.操作数据库，进行金额扣减
                userAccountMapper.minusAccountMoney(userId,amount);

                //3.记录用户account的流水明细表
                userAccountService.saveUserAccountDetail(SystemConstant.ACCOUNT_TRADE_TYPE_MINUS,userId,"消费操作:" + content,amount,orderNo);

                //4.删除我们于redis中存入的锁定金额数据对象
                redisTemplate.delete(accountLockResultKey);
            }
        } catch (Exception e) {
            //5.我们在执行业务的过程中如果遇到了错误，导致任务卡住
            // 我们就必须手动释放这个分布式锁，否则就会造成死锁
            log.error("在执行用户账户余额扣减的过程中失败了,释放分布式锁...");
            throw new GuiguException(201,"执行用户账户余额扣减的过程中失败");
        } finally {
            redisTemplate.delete(accountMoneyMinusRepeatLockKey);
        }

    }

    /**
     * 用户余额解锁
     * @param orderNo
     */
    @Override
    public void unlockAccountMoney(String orderNo) {
        //1.第一步，我们先从redis中，将锁定余额信息对象拿到
        String accountLockResultKey = "user:account:lockmoney:data:" + orderNo;
        String accountLockMoneyStr = redisTemplate.opsForValue().get(accountLockResultKey);

        if (StringUtils.isEmpty(accountLockMoneyStr)){
            return;
        }

        AccountLockResultVo accountLockResultVo = JSONObject.parseObject(accountLockMoneyStr, AccountLockResultVo.class);
        //2.获取关键数据
        //用户id
        Long userId = accountLockResultVo.getUserId();
        //锁定金额
        BigDecimal amount = accountLockResultVo.getAmount();
        //锁定信息
        String content = accountLockResultVo.getContent();


        //这里我们要进行解锁操作，我们也需要做一个幂等性保障
        //防止重复解锁，同样会导致数据库数据不一致
        String accountMoneyUnlockRepeatLockKey = "account:money:unlock:repeat:" + orderNo;
        try {
            //加载分布式锁
            Boolean isFirstTime = redisTemplate.opsForValue().setIfAbsent(accountMoneyUnlockRepeatLockKey, "1");
            if (isFirstTime) {
                //3.进行解锁
                userAccountMapper.unlockAccountMoney(userId,amount);

                //4.保存用户账户流水明细信息
                userAccountService.saveUserAccountDetail(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK,userId,"解锁操作：" + content,amount,orderNo);

                //5.将锁定信息从redis中删除
                redisTemplate.delete(accountLockResultKey);
            }
        } catch (Exception e) {
            throw new GuiguException(201, "释放账户锁定余额信息失败");
        } finally {
            //6.无论如何执行完最后要将分布式锁释放
            redisTemplate.delete(accountLockResultKey);
        }


    }
}